/* SPU Parallel Elliptic Curve Library - Provides Elliptic Curve Cryptographic
 * functions to the Synergistic Processing Unit target platform
 *
 * Copyright (C) 2008  Michael Annichiarico (mike.gcdeveloper@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include"field2m.h"
#include<string.h>

unsigned int  nist_233[] = {0x00000200,0x00000000,0x00000000,0x00000000,0x00000000,0x00000400,0x00000000,0x00000001};
///*NIST 233*/
//VECTOROF  nist_163[] = {0x00000008,0x00000000,0x00000000,0x00000000,0x00000000,0x000000C9};  /*NIST 163*/
//

void addrtest() {
	FIELD2M sa[2];
	DBLFIELD2M dbl;
	PDBLFIELD2M ptrA = (PDBLFIELD2M)sa;
	PFIELD2M ptrB = (PFIELD2M)(((PDBLFIELD2M)&dbl)+1);

	#define ps(x) printf("sizeof(" #x ") == %d bytes\n",sizeof(x));
	#define pa(x) printf(#x " = %016llx\n",x);

	pa(sa);
	pa(&(sa[0]));
	pa(&(sa[1]));
	pa(&dbl);
	pa(ptrA);
	pa(ptrB);

	ps(sa[0]);
	ps(dbl);
	ps(*ptrA);
	ps(*ptrB);
}

void main() {
	FIELD2M p;
	FIELD2M q;
	FIELD2M c;
//	FIELD2M _d[2];
//	PDBLFIELD2M d = _d;
	DBLFIELD2M dstatic,fstatic;
	PDBLFIELD2M d = &dstatic;
	PFIELD2M _d = (PFIELD2M)d;
	PDBLFIELD2M f = &fstatic;
	PFIELD2M _f = (PFIELD2M)f;
	int i;

	vec_ullong2 va = (vec_ullong2){0x8000000000000000ull,6};
	vec_ullong2 vb = (vec_ullong2){~0,1};
	vec_ullong2 vc = (vec_ullong2){~0,~1};
	vec_ullong2 vd = (vec_ullong2){~0,1};
	vec_ullong2 vans;
	vec_ullong2 zero = spu_splats(0ull);

	printf("vb %s vb\n",(vector_cmpeq(vb,vb)?"==":"!="));
	printf("vb %s vc\n",(vector_cmpeq(vb,vc)?"==":"!="));
	printf("vb %s vd\n",(vector_cmpeq(vb,vd)?"==":"!="));


	addrtest();

#define pvec(vp) printf("vector *(" #vp ") *(%016llx) == %016llx %016llx\n",vp,((unsigned long long*)vp)[0],((unsigned long long*)vp)[1])

	pvec(&va);
	pvec(&vb);
	pvec(&vc);
	vans=spu_and(va,vb);
	pvec(&vans);
	vans=spu_and(vb,vc);
	pvec(&vans);
	vans=spu_and(va,vc);
	pvec(&vans);

	memset(&p,0xAA,sizeof(p));
	memset(&q,0x55,sizeof(q));
	memset(d,0x0,sizeof(DBLFIELD2M));

	printf("poly_top(p) == %d\n",poly_findtop(&p));
	printf("poly_top(q) == %d\n",poly_findtop(&q));

#define printpoly(p) \
	printf("Poly " #p ": "); poly_print(p); printf("\n");
#define printnicepoly(p) \
	printf("Poly " #p ": "); poly_printnice(p); printf("\n");

	printnicepoly(&poly_zero);	
	printnicepoly(&poly_one);	
	printnicepoly(&poly_nist233);	

	printpoly(&p);
	printpoly(&q);
	printpoly(&_d[1]);
	printpoly(&_d[0]);

	p.v[0] = spu_splats(0ull);
	p.v[1] = spu_splats(0ull);
	q.v[0] = spu_splats(0ull);
	q.v[1] = spu_splats(0ull);
	p.e[1] = 0x5;
	q.e[1] = 0x3;

	printnicepoly(&p);
	printnicepoly(&q);

	poly_add(&c,&p,&q);
	printnicepoly(&c);
	memcpy(d,&c,sizeof(c));
	d->v[0]=(vec_ullong2){0x6000000000000001ull,0};
	d->v[1]=(vec_ullong2){0x6000000000000001ull,0};
	d->v[2]=zero;
	d->v[3]=zero;
	printf("TOP d0 == %d",poly_findtop(_d));
	printf("TOP d1 == %d",poly_findtop(_d+1));
	printpoly(_d);
	printnicepoly(_d);
	printf("degree = %d\n",poly_degree(_d));
	for(i=0;i<12;++i) {
		//poly_dblshiftl(d,d,3);
		poly_dblshiftl1(d,d);
		printpoly(_d+1);
		printpoly(_d);
		printnicepoly(_d+1);
		printf("degree = %d\n",poly_degree(_d+1));
		printnicepoly(_d);
		printf("degree = %d\n",poly_degree(_d));
	}
	printf("about to shift right by 4s\n");
	printnicepoly(_d+1);
	printnicepoly(_d);
	for(i=0;i<4;++i) {
		poly_dblshiftr(d,d,4);
		printpoly(_d+1);
		printpoly(_d);
		printnicepoly(_d+1);
		printf("degree = %d\n",poly_degree(_d+1));
		printnicepoly(_d);
		printf("degree = %d\n",poly_degree(_d));
	}
	
	memcpy(f,d,sizeof(*f));
		printnicepoly(_f+1);
		printnicepoly(_f);

	printf("MULTIPLYING! \n");
	printnicepoly(&p);
	printnicepoly(&q);
	poly_mult(d,&p,&q);
	printnicepoly(_d+1);
	printnicepoly(_d);
	printf("MULTIPLYING! again \n p after adding x^0:\n");
	poly_add(&p,&p,&poly_one);
	printnicepoly(&p);
	poly_shift(&p,&p,231); printf("p after << 231:\n");
	printnicepoly(&p);
	printnicepoly(&q);
	poly_mult(d,&p,&q);
	printnicepoly(_d+1);
	printnicepoly(_d);

	memcpy(d,f,sizeof(*f));

	printf("NIST 233 Reduce!\n");
	poly_reduce_nist233(f);
	printnicepoly(_f+1);
	printnicepoly(_f);

	printf("doing original plus reduced, to see if that then reduces to nist233:\n");
	poly_add(_d,_d,_f);
	poly_add(_d+1,_d+1,_f+1);
	printf("d+f ==\n");
	printnicepoly(_d+1);
	printnicepoly(_d);

	poly_reduce_nist233(d);
	printf("d+f REDUCED ==\n");
	printnicepoly(_d+1);
	printnicepoly(_d);

	{int i,j; DBLFIELD2M zero,PROD,nist;FIELD2M R;
	FIELD2M x9m1;
	printf("NIST 233 Reduce on NIST233, should == 0!!\n");
	memset(&nist,0,sizeof(DBLFIELD2M));
	memset(&zero,0,sizeof(DBLFIELD2M));
	memset(&PROD,0,sizeof(DBLFIELD2M));
	memset(f,0,sizeof(*f));
	for(i=0,j=((233+31)/32)/4-1;j>=0;j--,i++) {
		((unsigned int*)(f->e))[4*i] = nist_233[4*j];
		((unsigned int*)(f->e))[4*i+1] = nist_233[4*j+1];
		((unsigned int*)(f->e))[4*i+2] = nist_233[4*j+2];
		((unsigned int*)(f->e))[4*i+3] = nist_233[4*j+3];
		((unsigned int*)(nist.e))[4*i] = nist_233[4*j];
		((unsigned int*)(nist.e))[4*i+1] = nist_233[4*j+1];
		((unsigned int*)(nist.e))[4*i+2] = nist_233[4*j+2];
		((unsigned int*)(nist.e))[4*i+3] = nist_233[4*j+3];
	}
	printnicepoly(_f+1);
	printnicepoly(_f);
	poly_reduce_nist233(f);
	printnicepoly(_f+1);
	printnicepoly(_f);
	printf("trying nist^2 mod nist...:\n");
	poly_mult(f,&nist,&nist);
	printnicepoly(&nist);
	printf("nist^2:\n");
	printnicepoly(_f+1);
	printnicepoly(_f);
	poly_reduce_nist233(f);
	printf("nist^2 mod nist:\n");
	printnicepoly(_f+1);
	printnicepoly(_f);
	printf("---\n");
	for(i=0;i<0;++i) {
		FIELD2M R;
		memset(f,0,sizeof(*f));
		R.e[0] = (((unsigned long long)rand())<<(rand()%32)) | ((unsigned long long)rand());
		R.e[1] = (((unsigned long long)rand())<<(rand()%32)) | ((unsigned long long)rand());
		poly_mult(f,&nist,&R);
		poly_reduce_nist233(f);
	if(memcmp(f,&zero,sizeof(zero))!=0) {
		printf("ERROR, f!=0 at i=%d\n",i);
		poly_printnice(&R);
	}
	}
	poly_sqr(f,&nist);
	printf("nist^2 == \n");
	poly_printnice(_f);
	poly_printnice(_f+1);

	memset(&x9m1,0,sizeof(FIELD2M));
	x9m1.e[1] = 0xFull;
	memset(f,0,sizeof(DBLFIELD2M));
	memcpy(_f,&x9m1,sizeof(FIELD2M));
	printf("\n---\n");
	for(i=0;i<8;++i) {
		printnicepoly(_f);
		printnicepoly(_f+1);
		poly_sqr(f,_f);
		printf("\n-\n");
	}
	printf("mod inv tests ... results should be 1 mod nist233 since Rinv * R == 1\n");
	for(i=0;i<5;++i) {
		int k;
		FIELD2M R,Rinv;
		memset(f,0,sizeof(*f));
		memset(&R,0,sizeof(R));
		memset(&Rinv,0,sizeof(Rinv));
//		R.e[0] = (((unsigned long long)rand())<<(rand()%32)) | ((unsigned long long)rand());
		R.e[0] = 0ull;
		R.e[1] = 3ull;
		R.e[1]<<=i;
//		poly_mod_mult(&R,&R);
//		R.e[1] = (((unsigned long long)rand())<<(rand()%32)) | ((unsigned long long)rand());
//		R.e[1] = (((unsigned long long)rand())<<(rand()%32)) | ((unsigned long long)rand());
		printnicepoly(&R);
		k=1;
//		for(j=0;j<8;++j) { poly_shift(&R,&R,(j+1)*k); k*=-1; printnicepoly(&R); }
		poly_inv_mod(&Rinv,&R);
		printnicepoly(&Rinv);
		poly_mod_mult(f,&Rinv,&R);
		printnicepoly(f);
	}

	

	}

}

